package com.tiancheng.trade.payment.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.tiancheng.trade.commom.core.data.PageInfo;
import com.tiancheng.trade.commom.core.exception.error.BusinessErrorInfoEnum;
import com.tiancheng.trade.commom.core.enums.SettleStatusEnum;
import com.tiancheng.trade.payment.mapper.SettleFileMapper;
import com.tiancheng.trade.payment.model.SettleDetail;
import com.tiancheng.trade.payment.model.SettleDistError;
import com.tiancheng.trade.payment.model.SettleFile;
import com.tiancheng.trade.payment.service.SettleDetailService;
import com.tiancheng.trade.payment.service.SettleDistErrorService;
import com.tiancheng.trade.payment.service.SettleFileService;
import com.tiancheng.trade.payment.task.ums.bo.SettleDetailBo;
import com.tiancheng.trade.payment.task.ums.bo.SettleFileBo;
import com.tiancheng.trade.payment.util.CommonUtil;
import com.tiancheng.trade.payment.util.ExcelBuildUtil;
import com.tiancheng.trade.payment.vo.admin.SettleFileQueryVo;
import com.tiancheng.trade.payment.vo.admin.res.SettleFileResVo;
import com.tiancheng.trade.payment.vo.api.QueryDateSettleVO;
import com.tiancheng.trade.payment.vo.api.SettleCountAllVO;
import com.tiancheng.trade.payment.vo.api.SettleDetailVO;
import com.tiancheng.trade.payment.vo.api.SettleRefundVO;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author kellen
 * @date 2024-12-09 16:54
 */
@Service
public class SettleFileServiceImpl implements SettleFileService {
    @Autowired
    private SettleFileMapper settleFileMapper;
    @Autowired
    private SettleDetailService settleDetailService;
    @Autowired
    private SettleDistErrorService settleDistErrorService;

    @Override
    public List<SettleFile> findByFileNames(Set<String> fileNames) {
        QueryWrapper<SettleFile> conditions = new QueryWrapper<>();
        conditions.in("appr_file_name", fileNames).or().in("dist_file_name", fileNames);
        return settleFileMapper.selectList(conditions);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public int saveSettleApprRet(List<SettleFileBo> settleFileBos) {
        if (CollectionUtils.isEmpty(settleFileBos)) {
            return 0;
        }
        int i = settleFileMapper.updateBatch(settleFileBos);
        BusinessErrorInfoEnum.ASSET_MANAGEMENT_UPDATE_APPROPRIATIONS_ERROR.assertIsTrue(i > 0);
        for (SettleFileBo settleFileBo : settleFileBos) {
            final List<String> distIds = settleFileBo.getSettleDetailBos().stream().map(SettleDetail::getDistId).collect(Collectors.toList());
            final List<SettleDistError> settleDistErrors = settleDistErrorService.queryByBatchDistIds(distIds);
            if (!CollectionUtils.isEmpty(settleDistErrors)) {
                List<SettleDetailBo> settleDetailBos=new ArrayList<>();
                List<SettleDistError> updateSdes=new ArrayList<>();
                for (SettleDetailBo settleDetailBo : settleFileBo.getSettleDetailBos()) {
                    final List<SettleDistError> collect = settleDistErrors.stream().filter(e -> e.getBatchDistId().equals(settleDetailBo.getDistId())).collect(Collectors.toList());
                    if (CollectionUtils.isEmpty(collect)) {
                        // 不在抽佣异常表，
                        settleDetailBos.add(settleDetailBo);
                    } else {
                        // 在抽佣异常表
                        final SettleDistError settleDistError = new SettleDistError();
                        settleDistError.setId(collect.get(0).getId());
                        settleDistError.setDistStatus(settleDetailBo.getDistStatus());
                        if(null!=settleDetailBo.getDistAmount() && settleDetailBo.getDistAmount().intValue()>0){
                            settleDistError.setDistAmountSc(settleDetailBo.getDistAmount().intValue());
                        }
                        settleDistError.setDistDesc(settleDetailBo.getDistDesc());
                        settleDistError.setDistFailReason(settleDetailBo.getDistFailReason());
                        settleDistError.setUpdateTime(LocalDateTime.now());
                        updateSdes.add(settleDistError);
                    }
                }
                // 1、更新清分明细表、支付&退款明细表的清分状态
                if(!CollectionUtils.isEmpty(settleDetailBos)){
                    settleDetailService.updatePayFlowSettleStatus(settleDetailBos);
                    settleDetailService.updateBatch(settleDetailBos);
                }
                // 2、更新异常分账表的分账状态
                if(!CollectionUtils.isEmpty(updateSdes)){
                    settleDistErrorService.updateBatch(updateSdes);
                }
            } else {
                settleDetailService.updatePayFlowSettleStatus(settleFileBo.getSettleDetailBos());
                settleDetailService.updateBatch(settleFileBo.getSettleDetailBos());
            }
        }
        return i;
    }

    @Override
    public int save(SettleFile settleFile) {
        return settleFileMapper.insert(settleFile);
    }

    @Override
    public PageInfo<SettleFileResVo> findList(SettleFileQueryVo settleFileQueryVo) {
        if (settleFileQueryVo.getSettleDateStart() != null)
            settleFileQueryVo.setSettleDateTimeStart(settleFileQueryVo.getSettleDateStart().atStartOfDay());
        if (settleFileQueryVo.getSettleDateEnd() != null)
            settleFileQueryVo.setSettleDateTimeEnd(settleFileQueryVo.getSettleDateEnd().atStartOfDay().plusDays(1));
        // 缺省默认为每页10条
        settleFileQueryVo.setPageSize(settleFileQueryVo.getPageSize() == null ? 10 : settleFileQueryVo.getPageSize());
        // 缺省默认为从倒叙第一条开始
        int fileStart = settleFileQueryVo.getCurrentPage() == null ? 0 : (settleFileQueryVo.getCurrentPage() - 1) * settleFileQueryVo.getPageSize();
        settleFileQueryVo.setStartNum(fileStart);
        // 明细起始id
        long settleDetailStartId = 0L;
        List<QueryDateSettleVO> list;
        Map<Long, SettleFileResVo> resultMap = new LinkedHashMap<>();
        int total = settleFileMapper.countDateSettle(settleFileQueryVo);
        do {
            settleFileQueryVo.setStartSettleDetailId(settleDetailStartId);
            list = settleFileMapper.queryDateSettle(settleFileQueryVo);
            for (QueryDateSettleVO queryDateSettleVO : list) {
                settleDetailStartId = queryDateSettleVO.getId();
                if (resultMap.get(queryDateSettleVO.getSettleFileId()) == null) {
                    SettleFileResVo vo = new SettleFileResVo();
                    vo.setId(queryDateSettleVO.getSettleFileId());
                    vo.setSettleDate(queryDateSettleVO.getSettleDate()); // 清分日期
                    vo.setSettleMerchantCount(1); // 清分商户数
                    vo.setSettleAmount(queryDateSettleVO.getTargetApprAmount() > 0 ? queryDateSettleVO.getTargetApprAmount().intValue() : queryDateSettleVO.getApprAmount().intValue()); // 应清分金额
                    if (SettleStatusEnum.SUCCESS.getCode().equals(queryDateSettleVO.getApprStatus())) {
                        vo.setSettleSuccessAmount(vo.getSettleAmount()); // 实际清分成功金额
                        vo.setApprSuccessAmount(queryDateSettleVO.getApprAmount().intValue()); // 实际划付成功金额
                    } else {
                        vo.setSettleSuccessAmount(0);
                        vo.setApprSuccessAmount(0);
                    }
                    vo.setApprAmount(vo.getSettleAmount()); // 应划付金额
                    vo.setApprRefund(queryDateSettleVO.getRefundAmount() > 0 ? queryDateSettleVO.getRefundAmount().intValue() : 0); // 退款金额
                    vo.setDistAmount(queryDateSettleVO.getTargetDistAmount().intValue()); // 应抽佣金额
                    vo.setRefundDistAmount(queryDateSettleVO.getRefundDistAmount().intValue()); // 退款返还抽佣金额
                    if (SettleStatusEnum.SUCCESS.getCode().equals(queryDateSettleVO.getDistStatus())) {
                        vo.setDistSuccessAmount(queryDateSettleVO.getDistAmount().intValue()); // 实际抽佣成功金额
                    } else {
                        vo.setDistSuccessAmount(0);
                    }

                    resultMap.put(queryDateSettleVO.getSettleFileId(), vo);
                } else {
                    resultMap.get(queryDateSettleVO.getSettleFileId()).setSettleMerchantCount(resultMap.get(queryDateSettleVO.getSettleFileId()).getSettleMerchantCount() + 1); // 清分商户数
                    if ((queryDateSettleVO.getTargetApprAmount() > 0)) {
                        resultMap.get(queryDateSettleVO.getSettleFileId()).setSettleAmount(resultMap.get(queryDateSettleVO.getSettleFileId()).getSettleAmount() + queryDateSettleVO.getTargetApprAmount().intValue()); // 应清分金额
                        resultMap.get(queryDateSettleVO.getSettleFileId()).setApprAmount(resultMap.get(queryDateSettleVO.getSettleFileId()).getApprAmount() + queryDateSettleVO.getTargetApprAmount().intValue()); // 应划付金额
                    } else {
                        resultMap.get(queryDateSettleVO.getSettleFileId()).setSettleAmount(resultMap.get(queryDateSettleVO.getSettleFileId()).getSettleAmount() + queryDateSettleVO.getApprAmount().intValue()); // 应清分金额
                        resultMap.get(queryDateSettleVO.getSettleFileId()).setApprAmount(resultMap.get(queryDateSettleVO.getSettleFileId()).getApprAmount() + queryDateSettleVO.getApprAmount().intValue()); // 应划付金额
                    }
                    if (SettleStatusEnum.SUCCESS.getCode().equals(queryDateSettleVO.getApprStatus())) {
                        if ((queryDateSettleVO.getTargetApprAmount() > 0)) {
                            resultMap.get(queryDateSettleVO.getSettleFileId()).setSettleSuccessAmount(resultMap.get(queryDateSettleVO.getSettleFileId()).getSettleSuccessAmount() + queryDateSettleVO.getTargetApprAmount().intValue()); // 实际清分成功金额
                        } else {
                            resultMap.get(queryDateSettleVO.getSettleFileId()).setSettleSuccessAmount(resultMap.get(queryDateSettleVO.getSettleFileId()).getSettleSuccessAmount() + queryDateSettleVO.getApprAmount().intValue()); // 实际清分成功金额
                        }
                        resultMap.get(queryDateSettleVO.getSettleFileId()).setApprSuccessAmount(resultMap.get(queryDateSettleVO.getSettleFileId()).getApprSuccessAmount() + queryDateSettleVO.getApprAmount().intValue()); // 实际划付成功金额
                    }
                    if ((queryDateSettleVO.getRefundAmount() > 0)) {
                        resultMap.get(queryDateSettleVO.getSettleFileId()).setApprRefund(resultMap.get(queryDateSettleVO.getSettleFileId()).getApprRefund() + queryDateSettleVO.getRefundAmount().intValue()); // 退款金额
                    }
                    resultMap.get(queryDateSettleVO.getSettleFileId()).setDistAmount(resultMap.get(queryDateSettleVO.getSettleFileId()).getDistAmount() + queryDateSettleVO.getTargetDistAmount().intValue()); // 应抽佣金额
                    resultMap.get(queryDateSettleVO.getSettleFileId()).setRefundDistAmount(resultMap.get(queryDateSettleVO.getSettleFileId()).getRefundDistAmount() + queryDateSettleVO.getRefundDistAmount().intValue()); // 退款返还抽佣金额
                    if (SettleStatusEnum.SUCCESS.getCode().equals(queryDateSettleVO.getDistStatus())) {
                        resultMap.get(queryDateSettleVO.getSettleFileId()).setDistSuccessAmount(resultMap.get(queryDateSettleVO.getSettleFileId()).getDistSuccessAmount() + queryDateSettleVO.getDistAmount().intValue()); // 实际抽佣成功金额
                    }
                }
            }
        } while (list.size() > 999);
        list.clear();
        list = null;

        List<SettleFileResVo> vos = new ArrayList<>(resultMap.values());
        Collections.reverse(vos); // 倒叙
        resultMap.clear();
        resultMap = null;

        PageInfo<SettleFileResVo> pageRes = new PageInfo<>();
        pageRes.setList(vos);
        pageRes.setPagination(new PageInfo.Pagination(settleFileQueryVo.getCurrentPage(), settleFileQueryVo.getPageSize(), total));
        return pageRes;
    }

    @Override
    public void exportList(SettleFileQueryVo settleFileQueryVo, HttpServletResponse response, HttpServletRequest request) throws IOException {
        if (settleFileQueryVo.getId().isEmpty()) return;
        Map<String, Workbook> workbooks = new LinkedHashMap<>();
        if(settleFileQueryVo.getIsMerge()==0){
            // 不合并导出
            for (long id : settleFileQueryVo.getId()) {
                this.getWorkbook(new ArrayList<Long>(){
                    private static final long serialVersionUID = 7179105573960124730L;

                    {add(id);}}, settleFileQueryVo.getMerchantCode(), workbooks);
            }
        }else{
            // 合并为一个文件导出
            this.getWorkbook(settleFileQueryVo.getId(), settleFileQueryVo.getMerchantCode(), workbooks);
        }
        if (workbooks.size() == 1) {
            for (Map.Entry<String, Workbook> entry : workbooks.entrySet()) {
                if(settleFileQueryVo.getIsMerge()==0){
                    // 不合并导出
                    ExcelBuildUtil.exportExcelZip(request, response, workbooks, "清分文件.zip");
                    break;
                }
                ExcelBuildUtil.exportExcel(request, response, entry.getValue(), entry.getKey());
                break;
            }
        } else {
            ExcelBuildUtil.exportExcelZip(request, response, workbooks, "清分文件.zip");
        }
    }

    @Override
    public int updateNoneSettle(SettleFileBo settleFileBo) {
        QueryWrapper<SettleFile> conditions = new QueryWrapper<>();
        SettleFile updateEntity = new SettleFile();
        if (!StringUtils.isEmpty(settleFileBo.getApprFileName())) {
            // 划付
            conditions.eq("appr_file_name", settleFileBo.getApprFileName());
            updateEntity.setApprSuccessNum(0);
            updateEntity.setApprSuccessAmount(0L);
            updateEntity.setApprSuccessPart(0);
            updateEntity.setApprSuccessPartAmount(0L);
            updateEntity.setApprBackFileUrl(settleFileBo.getApprBackFileUrl());
            settleFileMapper.update(updateEntity, conditions);
        } else if (!StringUtils.isEmpty(settleFileBo.getDistFileName())) {
            conditions.eq("dist_file_name", settleFileBo.getDistFileName());
            updateEntity.setDistSuccessNum(0);
            updateEntity.setDistSuccessPart(0);
            updateEntity.setDistSuccessPartAmount(0L);
            updateEntity.setDistSuccessAmount(0L);
            updateEntity.setDistBackFileUrl(settleFileBo.getDistBackFileUrl());
            settleFileMapper.update(updateEntity, conditions);
        }
        return 0;
    }

    @Override
    public void setSettleError(String apprFileName) {
        QueryWrapper<SettleFile> conditions = new QueryWrapper<>();
        conditions.eq("appr_file_name", apprFileName);
        conditions.isNull("appr_success_num");
        final SettleFile settleFile = settleFileMapper.selectOne(conditions);

        final SettleFile sf = new SettleFile();
        sf.setApprSuccessNum(0);// 成功0
        sf.setApprSuccessAmount(0L);//成功金额
        sf.setApprFailNum(settleFile.getApprTotalNum());
        sf.setApprFailAmount(settleFile.getApprTotalAmount());
        sf.setApprActualAmount(0L);

        sf.setDistSuccessNum(0);
        sf.setDistSuccessAmount(0L);
        sf.setDistFailNum(sf.getDistTotalNum());
        conditions.eq("id",settleFile.getId());
        settleFileMapper.update(sf,conditions);

        settleDetailService.setApprAndDistError(apprFileName);
    }

    /**
     * 数据查询与表格生成
     *
     * @param ids        settle_file表的id
     * @param workbooks {@link List<Workbook>}
     */
    private void getWorkbook(List<Long> ids, String merchantCode, Map<String, Workbook> workbooks) {
        /**
         * XSSFWorkbook:
         * 优点：可以写入非常大量数据时，速度很快，占用内存少
         * 注意：过程中会产生临时文件，需要清理临时文件
         */
        Workbook workbook = new SXSSFWorkbook();

        String[] allTitle = {"划付企业用户号", "清分日期", "划付指令id", "划付金额(元)", "划付附言", "当前划付状态", "分账指令id", "分账金额(元)", "分账附言", "当前分账状态"};

        String[] sdTitle = {"订单名称", "支付订单号", "支付流水号", "子订单应收金额(元)", "子订单实收金额(元)", "支付时间", "主商户名称", "子订单号"
                , "子订单支付流水号", "清分企业用户号", "应清分日期", "清分时间", "划付指令ID", "划付附言", "当前划付状态", "分账指令ID", "分账附言", "当前分账状态"};

        String[] rdlTitle = {"划付指令ID", "清分企业用户号", "退款流水号", "原支付流水号", "原支付子订单号", "退款时间", "主商户名称", "退款金额(元)"};

        // 清分汇总查询
        List<SettleCountAllVO> allVOList = settleFileMapper.listCountAll(ids, merchantCode);
        if (allVOList.isEmpty()) {
            ExcelBuildUtil.addSheet(workbook, "清分汇总表", allTitle, new ArrayList<>(), true);
            ExcelBuildUtil.addSheet(workbook, "清分支付流水明细表", sdTitle, new ArrayList<>(), true);
            ExcelBuildUtil.addSheet(workbook, "清分退款流水明细表", rdlTitle, new ArrayList<>(), true);
            workbooks.put("清分文件", workbook);
            return;
        }

        workbooks.put("清分文件" + allVOList.get(0).getSettleDate().toLocalDate(), workbook); // 每一个工作簿以及对应的名称
        List<Map<String, Object>> dataList = new ArrayList<>();
        for (SettleCountAllVO settleCountAllVO : allVOList) {
            dataList.add(new LinkedHashMap<String, Object>() {{
                put("apprEnterpriseCode", settleCountAllVO.getApprEnterpriseCode());
                put("settleDate", null == settleCountAllVO.getSettleDate() ? null : settleCountAllVO.getSettleDate().toLocalDate());
                put("apprId", settleCountAllVO.getApprId());
                put("apprAmount", null == settleCountAllVO.getApprAmount() ? null : CommonUtil.cent2YuanStr(settleCountAllVO.getApprAmount()));
                put("apprDesc", settleCountAllVO.getApprDesc());
                put("apprStatus", "SUCCESS".equals(settleCountAllVO.getApprStatus()) ? "成功" : ("ING".equals(settleCountAllVO.getApprStatus()) ? "进行中" : ("ERROR".equals(settleCountAllVO.getApprStatus()) ? "失败" : "")));
                put("distId", settleCountAllVO.getDistId());
                put("distAmount", null == settleCountAllVO.getDistAmount() ? null : CommonUtil.cent2YuanStr(settleCountAllVO.getDistAmount()));
                put("distDesc", settleCountAllVO.getDistDesc());
                put("distStatus", "SUCCESS".equals(settleCountAllVO.getDistStatus()) ? "成功" : ("ING".equals(settleCountAllVO.getDistStatus()) ? "进行中" : ("ERROR".equals(settleCountAllVO.getDistStatus()) ? "失败" : "")));
            }});
        }
        ExcelBuildUtil.addSheet(workbook, "清分汇总表", allTitle, dataList, true);
        allVOList.clear();
        allVOList = null;
        dataList.clear();
        // 清分支付流水查询
        List<SettleDetailVO> sList = settleFileMapper.listSettleDetail(ids, merchantCode);
        for (SettleDetailVO settleDetailVO : sList) {
            dataList.add(new LinkedHashMap<String, Object>() {{
                put("orderName", settleDetailVO.getOrderName());
                put("paymentId", settleDetailVO.getPaymentId());
                put("flowId", settleDetailVO.getFlowId());
                put("orderAmount", null == settleDetailVO.getOrderAmount() ? null : CommonUtil.cent2YuanStr(settleDetailVO.getOrderAmount()));
                put("amount", null == settleDetailVO.getAmount() ? null : CommonUtil.cent2YuanStr(settleDetailVO.getAmount()));
                put("flowTime", null == settleDetailVO.getFlowTime() ? null : DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(settleDetailVO.getFlowTime()));
                put("merchantName", settleDetailVO.getMerchantName());
                put("subOrderId", settleDetailVO.getSubOrderId());
                put("subOrderFlowId", settleDetailVO.getSubOrderFlowId());
                put("apprEnterpriseCode", settleDetailVO.getApprEnterpriseCode());
                put("expectSettleDate", settleDetailVO.getExpectSettleDate());
                put("settleDate", settleDetailVO.getSettleDate());
                put("apprId", settleDetailVO.getApprId());
                put("apprDesc", settleDetailVO.getApprDesc());
                put("apprStatus", "SUCCESS".equals(settleDetailVO.getApprStatus()) ? "成功" : ("ING".equals(settleDetailVO.getApprStatus()) ? "进行中" : ("ERROR".equals(settleDetailVO.getApprStatus()) ? "失败" : "")));
                put("distId", settleDetailVO.getDistId());
                put("distDesc", settleDetailVO.getDistDesc());
                put("distStatus", "SUCCESS".equals(settleDetailVO.getDistStatus()) ? "成功" : ("ING".equals(settleDetailVO.getDistStatus()) ? "进行中" : ("ERROR".equals(settleDetailVO.getDistStatus()) ? "失败" : "")));
            }});
        }
        ExcelBuildUtil.addSheet(workbook, "清分支付流水明细表", sdTitle, dataList, true);
        sList.clear();
        sList = null;
        dataList.clear();
        // 清分退款流水查询
        List<SettleRefundVO> rList = settleFileMapper.listRefund(ids, merchantCode);
        for (SettleRefundVO settleRefundVO : rList) {
            dataList.add(new LinkedHashMap<String, Object>() {{
                put("apprId", settleRefundVO.getApprId());
                put("apprEnterpriseCode", settleRefundVO.getApprEnterpriseCode());
                put("flowId", settleRefundVO.getFlowId());
                put("payFlowId", settleRefundVO.getPayFlowId());
                put("subOrderId", settleRefundVO.getSubOrderId());
                put("flowTime", null == settleRefundVO.getFlowTime() ? null : DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(settleRefundVO.getFlowTime()));
                put("merchantName", settleRefundVO.getMerchantName());
                put("refundAmount", null == settleRefundVO.getRefundAmountDetail() ? (null == settleRefundVO.getRefundAmount()
                        ? null : CommonUtil.cent2YuanStr(settleRefundVO.getRefundAmount()))
                        : CommonUtil.cent2YuanStr(settleRefundVO.getRefundAmountDetail() + settleRefundVO.getRefundPlatformAmountDetail()));
            }});
        }
        ExcelBuildUtil.addSheet(workbook, "清分退款流水明细表", rdlTitle, dataList, true);
        dataList.clear();
        dataList = null;
    }


}
